﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zcold.ZcoldString;

namespace SimulinkParser
{
	/// <summary> SimulinkParameter with Name and Value
	/// </summary>
	public class SimulinkParameter
	{
		/// <summary> Parameter name
		/// </summary>
		public string Name;

		/// <summary> Parameter value
		/// </summary>
		public string Value;

		/// <summary> Construct an empty SimulinkParameter instance
		/// </summary>
		public SimulinkParameter() { Name = ""; Value = ""; }

		/// <summary> Construct a SimulinkParameter instance with specified name and value
		/// </summary>
		/// <param name="Name"> Parameter name </param>
		/// <param name="Value"> Parameter value </param>
		public SimulinkParameter(string Name, string Value)
		{
			this.Name = Name;
			this.Value = Value;
		}

		/// <summary> Construct a SimulinkParameter instance by parsing one string
		/// </summary>
		/// <param name="ParameterString"> String to be parsed </param>
		public SimulinkParameter(string ParameterString)
		{
			string CurrentLine = ParameterString;

			CurrentLine = CurrentLine.Replace('\t', ' ');
			CurrentLine = CurrentLine.Replace(", ", ",");
			CurrentLine = CurrentLine.RemoveWhitespaceFromStart();
			CurrentLine = CurrentLine.MergeChar(' ');
			Name = CurrentLine.Split(' ')[0];
			Value = CurrentLine.Substring(CurrentLine.IndexOf(' ') + 1);
			Value = Value.PurgeOneChar('"');
			//Console.WriteLine(Name + " :: " + Value);

		}
	}

	/// <summary> Parameter list with manipulation methods
	/// </summary>
	public class ParameterList
	{
		/// <summary> Number of parameters in the list
		/// </summary>
		public int Count { get { return Parameters.Count; } }

		/// <summary> Add a parameter to the end of the parameter list
		/// </summary>
		/// <param name="P"> Parameter to be added </param>
		public void Add(SimulinkParameter P) { Parameters.Add(P); }

		/// <summary> Add a parameter described by Name and Value to the end of the parameter list
		/// </summary>
		/// <param name="Name"> Parameter name </param>
		/// <param name="Value"> Parameter value </param>
		public void Add(string Name, string Value) { Parameters.Add(new SimulinkParameter(Name, Value)); }

		/// <summary> Set of parameters
		/// </summary>
		public List<SimulinkParameter> Parameters;

		/// <summary> Clear parameter list
		/// </summary>
		public void Clear() { Parameters = new List<SimulinkParameter>(); }

		/// <summary> Construct an empty parameter list
		/// </summary>
		public ParameterList() { Clear(); }

		/// <summary> Construct the parameter list from a set of parameters 
		/// </summary>
		/// <param name="SourceParameters"> Source parameter set </param>
		public ParameterList(List<SimulinkParameter> SourceParameters)
		{
			Clear();
			Clone(SourceParameters);
		}

		/// <summary> Construct the parameter list from another ParameterList
		/// </summary>
		/// <param name="Source"> Source parameter list to be cloned </param>
		public ParameterList(ParameterList Source)
		{
			Clear();
			Clone(Source);
		}

		/// <summary> Clone from another ParameterList
		/// </summary>
		/// <param name="Source"> Source ParameterList </param> 
		public void Clone(ParameterList Source)
		{
			Clear();
			foreach (SimulinkParameter P in Source.Parameters) { Parameters.Add(new SimulinkParameter(P.Name, P.Value)); }
		}

		/// <summary> Clone from a list of parameters
		/// </summary>
		/// <param name="SourceParameters"> Source Parameters </param> 
		public void Clone(List<SimulinkParameter> SourceParameters)
		{
			Clear();
			foreach (SimulinkParameter P in SourceParameters) { Parameters.Add(new SimulinkParameter(P.Name, P.Value)); }
		}

		/// <summary> Return all parameters as one string
		/// </summary>
		/// <returns> One string contains all parameters </returns>
		public override string ToString()
		{
			string Result = "";

			for (int i = 0; i < Parameters.Count; i++) Result += "\n    " + Parameters[i].Name + ", " + Parameters[i].Value;

			return Result;
		}

		/// <summary> Return the parameter with the specified name
		/// </summary>
		/// <param name="ParameterName"> Specified parameter name </param>
		/// <returns> Parameter with the specified name or with name ParameterNameNotFound </returns>
		public SimulinkParameter LocateParameterByName(string ParameterName)
		{
			foreach (SimulinkParameter P in Parameters) if (P.Name.Equals(ParameterName)) return P;
			throw new NoParameterException();
		}

		/// <summary> Check if parameter with specified name is in the list
		/// </summary>
		/// <param name="ParameterName"></param>
		/// <returns></returns>
		public bool ContainsParameterByName(string ParameterName)
		{
			if (Parameters.Count == 0) return false;
			foreach (SimulinkParameter P in Parameters)
				if (P.Name.Equals(ParameterName)) return true;

			return false;
		}

		/// <summary> Check if parameters with specified names are all in the list
		/// </summary>
		/// <param name="ParameterNames"></param>
		/// <returns></returns>
		public bool ContainsParameterNames(List<string> ParameterNames)
		{
			foreach (string ParameterName in ParameterNames)
				if (!ContainsParameterByName(ParameterName)) return false;

			return true;
		}
	}

	/// <summary> Parameters
	/// </summary>
	public class ParameterBlock
	{
		/// <summary> ParameterBlock name
		/// </summary>
		public string Title;

		/// <summary> Child blocks
		/// </summary>
		public List<ParameterBlock> Childs;

		/// <summary> Does this ParameterBlock instance has child blocks?
		/// <para> true: yes, false: no </para>
		/// </summary>
		public bool HaveChild { get { if (Childs.Count > 0) return true; else return false; } }

		/// <summary> Parameters
		/// </summary>
		public ParameterList Parameters;

		/// <summary> Locate a parameter in Parameters
		/// </summary>
		/// <param name="ParameterName"> Parameter name </param>
		/// <returns> SimulinkParamter instance with a name = ParameterName </returns>
		public SimulinkParameter FindParameterByName(string ParameterName)
		{
			return Parameters.LocateParameterByName(ParameterName);
		}

		/// <summary> Locate a parameter value in Parameters
		/// </summary>
		/// <param name="ParameterName"> Parameter name </param>
		/// <returns> Parameter value </returns>
		public string FindParameterValueByName(string ParameterName)
		{
			return Parameters.LocateParameterByName(ParameterName).Value;
		}

		/// <summary> Return SrcBlock parameter's value
		/// </summary>
		public string SrcBlock { get { return FindParameterValueByName("SrcBlock"); } }

		/// <summary> Return SrcPort parameter's value
		/// </summary>
		public string SrcPort { get { return FindParameterValueByName("SrcPort"); } }

		/// <summary> Return DstBlock parameter's value
		/// </summary>
		public string DstBlock { get { return FindParameterValueByName("DstBlock"); } }

		/// <summary> Return DstPort parameter's value
		/// </summary>
		public string DstPort { get { return FindParameterValueByName("DstPort"); } }

		/// <summary> Locate a child ParameterBlock with a specified name
		/// </summary>
		/// <param name="ChildName"> Child ParameterBlock instance name </param>
		/// <returns> ParameterBlock instance </returns>
		public ParameterBlock FindChildByName(string ChildName)
		{
			foreach (ParameterBlock pb in Childs)
				if (pb.Title.Equals(ChildName)) return pb;

			return new ParameterBlock();
		}

		/// <summary> Locate all child ParameterBlocks with a specified name
		/// </summary>
		/// <param name="ChildName"> Child ParameterBlock instance name </param>
		/// <returns> ParameterBlock instances </returns>
		public List<ParameterBlock> FindChildListByName(string ChildName)
		{
			List<ParameterBlock> Result = new List<ParameterBlock>();

			foreach (ParameterBlock pb in Childs)
				if (pb.Title.Equals(ChildName)) Result.Add(pb);

			return Result;
		}

		/// <summary> Return Blocks
		/// </summary>
		public List<ParameterBlock> BlockChilds { get { return FindChildListByName("Block"); } }

		/// <summary> Return Lines
		/// </summary>
		public List<ParameterBlock> LineChilds { get { return FindChildListByName("Line"); } }

		/// <summary> Return Branches
		/// </summary>
		public List<ParameterBlock> BranchChilds { get { return FindChildListByName("Branch"); } }

		/// <summary> Return System
		/// </summary>
		public ParameterBlock System { get { return FindChildByName("System"); } }

		/// <summary> Return Default blocks
		/// </summary>
		public ParameterBlock BlockParameterDefaults { get { return FindChildByName("BlockParameterDefaults"); } }

		/// <summary> Clear all varaibles
		/// </summary>
		public void Clear()
		{
			Childs = new List<ParameterBlock>();
			Parameters = new ParameterList();
			Title = "";
		}

		/// <summary> Construct an empty ParameterBlock
		/// </summary>
		public ParameterBlock() { Clear(); }

		/// <summary> Construct a ParameterBlock by supplying child ParameterBlocks and ParameterList
		/// </summary>
		/// <param name="Title"> Name of this ParameterBlock </param>
		/// <param name="Child"> Child ParameterBlocks </param>
		/// <param name="Parameters"> ParameterList </param>
		public ParameterBlock(string Title, List<ParameterBlock> Child, ParameterList Parameters)
		{
			Clear();
			this.Title = Title;
			this.Childs.AddRange(Childs);
			this.Parameters.Clone(Parameters);
		}

		/// <summary> Check if this ParameterBlock contains a certain child with a specified child name
		/// </summary>
		/// <param name="ChildName"> Specified child name </param>
		/// <returns> true: yes, false: no </returns>
		public bool ContainsChildByName(string ChildName)
		{
			foreach (ParameterBlock PB in Childs)
				if (PB.Title.Equals(ChildName)) return true;
			return false;
		}

		/// <summary> Check if this ParameterBlock contains System
		/// </summary>
		/// <returns> true: yes, false: no </returns>
		public bool ContainsSystem { get { return ContainsChildByName("System"); } }

		/// <summary> Check if this ParameterBlock contains Block
		/// </summary>
		/// <returns> true: yes, false: no </returns>
		public bool ContainsBlock { get { return ContainsChildByName("Block"); } }

		/// <summary> Check if this ParameterBlock contains Branch
		/// </summary>
		/// <returns> true: yes, false: no </returns>
		public bool ContainsBranch { get { return ContainsChildByName("Branch"); } }

		/// <summary> Check if this ParameterBlock contains Line
		/// </summary>
		/// <returns> true: yes, false: no </returns>
		public bool ContainsLine { get { return ContainsChildByName("Line"); } }

		/// <summary> Print detailed information to console
		/// </summary>
		/// <param name="Level"> Print indent level <para> Higher level = more indent to the right </para> </param>
		public void PrintInfo(int Level)
		{
			for (int i = 0; i < Level; i++)
				Console.Write(" ");

			Console.WriteLine("Title: " + Title);

			for (int i = 0; i < Level; i++)
				Console.Write(" ");

			Console.WriteLine("Parameters: ...");
			foreach (SimulinkParameter SP in Parameters.Parameters)
			{
				for (int i = -1; i < Level; i++)
					Console.Write(" ");

				Console.WriteLine(SP.Name + " :: " + SP.Value);
			}

			for (int i = 0; i < Level; i++)
				Console.Write(" ");

			if (!HaveChild) Console.WriteLine("No child.");
			else
			{
				Console.WriteLine("Childs: ");
				foreach (ParameterBlock PB in Childs)
				{
					PB.PrintInfo(Level + 1);
				}
			}

		}
	}
}
